/******************************/
/* 描述：策划表生成工具 */
/* 作者：by 杉木说 */
/* 日期：2022-12-01 */
/* 修改：2023-04-05 */
/******************************/

const Utils = require("./Utils");
const fs = require('fs');
const zlib = require('zlib');
const nodeXlsx = require("node-xlsx");
const path = require('path');
const os = require('os');
const { Readable } = require('stream');
const moment = require('moment');
/*******************************************************/
/************************生成配置***********************/
/*******************************************************/

/** 
 * 前端配置文件分离生成 
 * true: 每张sheet表单独生成一个文件
 * false：所有工作簿的所有sheet表生成在一个文件中 
 */
let clientFileSplit;
/** 前端不分离模式的生成的配置文件名 */
let clientConfigName;
/** 前端配置文件后缀 */
let postfix;
/** 忽略的表Sheet */
let ingnoreSheet;
/** 生成配置文件时使用的加密的密钥 */
let encryptCode;
let additionalEncryptCode;
/** 策划表目录的名字 */
let excelSheetRootDirectory;
/** 生成配置文件后需要将其复制到哪个目录去 */
let copyGeneratedConfigToDir;

/** 前端文件配置输出根路径 */
let clientRootPath = "Gen-Client";
/** 后端文件配置输出根路径 */
let serverRootPath = "Gen-Server";
/** 代表策划表的后缀，只会读取这些文件中的配置 */
let excelExtArr = [".xls", ".xlsx"];
/** 记录数据 */
let clientFileSplitCount = 0, clientFileSplitTotal = 0, errorCount = 0;
/** 策划表根目录 */
let sheetsRootPath;

/** 客户端配置表存储的对象 */
let clientConfigTable = null;
let serverConfigTable = null;

/** C#代码内容 */
let CSharpCodeContent = `/*****************************************************
* ---佛祖保佑-永不宕机-永无BUG---
*   功能           根据策划表自动生成的C#脚本,请自行修改类型,不要直接替换!
*                  即number,list和object类型

*   作者           杉木说
*   时间           2023.8.06
*******************************************************/
using System.Collections.Generic;

// 主机名字: ${os.hostname()}
// 系统CPU的架构: ${os.arch()}
// 操作系统: ${os.type()}
// 操作系统名: ${os.platform()}
// 操作系统发行版本: ${os.release()}
// 自动生成时间: ${moment().format('YYYY年MM月DD日 HH:mm:ss')}`;

/*******************************************************/
/***********************执行方法************************/
/*******************************************************/

/**
 * 数字和字母映射
 */
const numberMapCharTable = {
    0: "A", 1: "B", 2: "C", 3: "D", 4: "E", 5: "F", 6: "G", 7: "H", 8: "I", 9: "J", 10: "K", 11: "L", 12: "M", 13: "N", 14: "O",
    15: "P", 16: "Q", 17: "R", 18: "S", 19: "T", 20: "U", 21: "V", 22: "W", 23: "X", 24: "Y", 25: "Z",
    get: function (val) {
        val = parseInt(val);
        if (val >= 0 && val <= 25) {
            return this[val];
        }
        return ++val;
    }
}
/**
 * 类型
 */
const TypeEnum = {
    string: "string", bool: "bool", list: "list", object: "object", number: "number",
    getToString() {
        return `${this.string}|${this.bool}|${this.list}|${this.object}|${this.number}`;
    },
    isIllegal(type) {
        return type != this.string && type != this.bool && type != this.list && type != this.object && type != this.number
    }
}

start();

/**
 * 获取工具的配置文件
 */
function getToolConfig() {
    let cfgJson = Utils.getToolConfigs();
    clientFileSplit = cfgJson.GenerationConfig.ClientConfigSplit;
    clientConfigName = cfgJson.GenerationConfig.ClientGenConfigName;
    postfix = cfgJson.GenerationConfig.ClientGenConfigPostfix;
    ingnoreSheet = cfgJson.GenerationConfig.IngnoreSheetName;
    encryptCode = cfgJson.GenerationConfig.EncryptCode;
    additionalEncryptCode = cfgJson.GenerationConfig.AdditionalEncryptCode;
    excelSheetRootDirectory = cfgJson.GenerationConfig.ExcelSheetRootDirectory;
    copyGeneratedConfigToDir = cfgJson.Other.CopyGeneratedConfigToDir;
}

/**
 * 入口方法 读表处理数据
 */
function start() {
    let tips = fs.readFileSync(path.join(__dirname, "..", "策划文档", "生成工具说明.txt"), "utf-8");
    console.log(tips);

    getToolConfig();
    sheetsRootPath = path.join(__dirname, "..", excelSheetRootDirectory);
    // 获取当前目录所有的文件
    let allDirArr = fs.readdirSync(sheetsRootPath);
    let successAllConfig = true;
    clientConfigTable = [];
    serverConfigTable = [];

    console.log("Start...\n\n@@@ Tips1 ############################\n");

    let joinCount = 0;
    // 先输出
    for (let index = 0; index < allDirArr.length; index++) {
        let file = allDirArr[index];
        let filePath = sheetsRootPath + "/" + file;
        let fileExt = path.parse(filePath).ext;
        let filename = path.parse(filePath).name;

        // 判断哪些文件不需要被读取
        if (excelExtArr.indexOf(fileExt) == -1) {
            console.log(`Tips: [${filename}${fileExt}] 文件不是配置表格式!不会处理该文件!`);
            continue;
        } else {
            if (filename.startsWith("~$")) {
                console.log("Tips: 打开的临时文件不会被处理(保存了的源文件还是会被处理的): 策划表根目录/" + file);
            } else {
                joinCount++;
                console.log("该表将加入队列: 策划表根目录/" + file);
            }
        }
    }
    console.log("---");
    console.log("队列中表数量:" + joinCount + "张");
    console.log("\n@@@ Tips2 ############################\n");

    let handleCount = 0;
    // 查找所有的需要的Excel文件
    for (let index = 0; index < allDirArr.length; index++) {
        let file = allDirArr[index];
        let filePath = sheetsRootPath + "/" + file;
        let fileExt = path.parse(filePath).ext;
        let filename = path.parse(filePath).name;

        // 判断哪些文件不需要被读取
        if (excelExtArr.indexOf(fileExt) == -1) {
            continue;
        }

        let sheets = nodeXlsx.parse(filePath);
        if (!filename.startsWith("~$")) {
            let success = getSheetsData(sheets, filename);
            if (!success) {
                successAllConfig = false;
                break;
            } else {
                console.log(`Tips: [${++handleCount}/${joinCount}]读取Excel表数据成功... ${filename}.xlsx`);
            }
        }
    }

    // 有错误则不生成配置文件
    if (!successAllConfig) {
        console.log(`\n!!! 策划配置表出现错误[严重的错误:[${errorCount}]]!配置文件生成失败,请查看上面的错误并修复!!!`);
        return;
    }

    if (Utils.isNullOrEmpty(clientConfigTable) || Utils.isNullOrEmpty(serverConfigTable)) {
        console.log("没有任何数据需要导出!!!");
        return;
    }
    clientConfigTable = clientConfigTable.substring(1);
    serverConfigTable = serverConfigTable.substring(1);

    clientConfigTable = "{" + clientConfigTable + "}";
    serverConfigTable = "{" + serverConfigTable + "}";

    console.log("\n==========================================");
    console.log("========+++= Excel表解析成功!!! =+++=======");
    console.log("==========================================\n");

    // 输出 前端后端配置文件
    startGenServerConfig();
    startGenClinetConfig();
}

/**
 * 生成后端配置
 */
function startGenServerConfig() {
    let __checkDir = path.join(__dirname, "..", serverRootPath);
    serverConfigTable = JSON.parse(serverConfigTable);
    // 每个配置表单独生成 json文件
    for (const key in serverConfigTable) {
        if (Object.hasOwnProperty.call(serverConfigTable, key)) {
            const cfg = serverConfigTable[key];
            let singleJsonStr = JSON.stringify(cfg);

            // 每个配置表单独输出
            let __path = path.join(__checkDir, key + ".json");

            // 处理未存在的文件夹
            Utils.checkAndMkDir(__checkDir);

            fs.writeFileSync(__path, singleJsonStr);
        }
    }
    console.log("[Server] 配置文件输出成功! " + __checkDir.toUpperCase());
}

/**
 * 开始输出前端配置文件
 */
function startGenClinetConfig() {
    // 配置不分离
    clientOutput2Unity(clientConfigTable, { split: false });

    // 每个配置表单独生成
    if (clientFileSplit) {
        let jsonObj = JSON.parse(clientConfigTable);
        clientFileSplitTotal = Object.keys(jsonObj).length;

        for (const key in jsonObj) {
            if (Object.hasOwnProperty.call(jsonObj, key)) {
                const cfg = jsonObj[key];
                clientOutput2Unity(JSON.stringify(cfg), { name: key, split: true });
            }
        }
    }
}

/**
 * 获取配置表真正需要的名字
 * 以 “$$Comment” 开头的表为调试表,不会输出到配置文件中
 * 
 * @param {*} sheetName sheet表名
 * @param {*} fileName 表的文件名
 * @returns 
 */
function getRealSheetName(sheetName, fileName) {
    // 以sheet或Sheet开头表示为默认的表,返回文件名
    if (sheetName.startsWith("Sheet") || sheetName.startsWith("sheet")) {
        return fileName;
    }
    // 返回用户自定义的表名 
    return `${fileName}_${sheetName}`;
}

/**
 * 自动生成C#类名
 * @param {*} filename 
 * @param {*} sheetName 
 */
function getAutoCreateClassName(filename, sheetName, oneSheet) {
    let transfromFileName = transformString(filename);
    let transfromSheetName = transformString(sheetName);
    if (oneSheet && (sheetName.startsWith("sheet") || sheetName.startsWith("Sheet"))) {
        return transfromFileName;
    } else {
        return transfromFileName + transfromSheetName;
    }
}

/** 变换字符串 */
function transformString(inputString) {
    // 以_分割字符串并转换为数组
    let words = inputString.split('_');
    // 遍历数组中的每个单词，并将首字母大写
    for (let i = 0; i < words.length; i++) {
        words[i] = words[i].charAt(0).toUpperCase() + words[i].substring(1);
    }
    // 将数组中的单词重新组合成一个字符串
    let result = words.join('');
    return result;
}

/**
 * 获取配置表数据
 * 
 * @param {*} sheets 
 * @param {*} filename 
 * @returns 
 */
function getSheetsData(sheets, filename) {
    let isAllSuccessful = true;
    let clientConfigList = [];
    let serverConfigList = [];
    let oneSheet = sheets.length == 1;
    // 解析所有sheet - 这里再处理一张Excel表中每一张Sheet
    for (let iii = 0; iii < sheets.length; iii++) {
        const sheet = sheets[iii];
        let sheetName = getRealSheetName(sheet.name, filename);
        let sheetPath = filename + ".xlsx " + sheet.name;
        let autoCreateClassName = getAutoCreateClassName(filename, sheet.name, oneSheet);
        let csharpClassInfo = `/// <summary>\n/// 策划表自动生成类 -->> ${sheetPath}\n/// </summary>\npublic class ${autoCreateClassName} \n{\n`;
        // console.log(csharpClassInfo);
        // 忽略的工作表不会被输出sheetDataArr
        if (sheet.name == ingnoreSheet) {
            console.log(`Tips: [${sheetPath}] 是被忽略的工作表,不会被输出!`);
            continue;
        }
        // 当前表所有的行数据
        let allRows = sheet.data
        let clientSheetDataArr = [];
        let serverSheetDataArr = [];
        // 前3列字段 1-字段名 2-字段信息描述 3-字段和输出类型
        let fieldArr = allRows[0];
        let fieldTypeArr = allRows[2];
        let fieldDescArr = allRows[1];
        // 拿到字段信息
        for (let index = 0; index < fieldArr.length; index++) {
            // const field = fieldArr[index];
            const { n: type, o } = getCSharpValueType(fieldTypeArr[index], fieldArr[index], sheetPath, index);
            if (type == null) {
                continue;
            }
            const comment = fieldDescArr[index].split("\n").join("\t// ");
            // console.log(comment);
            const fieldDefine = `\t// ${comment}\n\tpublic ${type} ${fieldArr[index]};`;
            csharpClassInfo += fieldDefine + "\n";
        }
        csharpClassInfo += "}";
        CSharpCodeContent += "\n\n" + csharpClassInfo;
        // console.log(csharpClassInfo);
        // 遍历每一行的数据 
        for (var i = 3; i < allRows.length; i++) {
            let row = allRows[i];
            let clientRowDataArr = [];
            let serverRowDataArr = [];
            if (row && row.length == 0) {
                // console.log(`Tips: ${sheetPath} => 第[${i + 1}]行 为空数据!!!`);
                continue;
            }
            // 是否有类型检查错误
            let hasTypeCheckError = false;
            // 该行每列的的数据 [以字段长度为数据长度]
            for (let index = 0; index < fieldArr.length; index++) {
                let columnData = row[index];

                // 注释行不会被输出和计算
                if (typeof columnData == "string" && columnData.startsWith("$$注释行$$-")) {
                    // console.log(columnData);
                    break;
                }

                // console.log(`${sheetName}表  第${i + 1}行 第${index + 1}列的 数据: ${columnData}`)
                let { output_type, field_type, hasError: typeError } = getOutAndFieldType(fieldTypeArr[index], fieldArr[index], sheetPath, index);

                // 有输出类型错误则标记错误,然后继续检查其他的
                if (typeError) {
                    isAllSuccessful = false;
                    hasTypeCheckError = true;
                    continue;
                }
                let { hasError, newData } = handleFieldVal(columnData, field_type, fieldArr[index], sheetPath, i, index);

                // 有错误则标记错误,然后继续检查其他的
                if (hasError) {
                    isAllSuccessful = false;
                    continue;
                }

                // 将处理过的列数据放入行的数组 -根据输出类型添加
                switch (parseInt(output_type)) {
                    case 1:
                        clientRowDataArr.push(newData);
                        break;
                    case 2:
                        serverRowDataArr.push(newData);
                        break;
                    case 3:
                        clientRowDataArr.push(newData);
                        serverRowDataArr.push(newData);
                        break;
                }
            }

            if (hasTypeCheckError) {
                break;
            }

            // 封装成对象
            let tempObj = `{${clientRowDataArr.join(",")}}`
            let serverTempObj = `{${serverRowDataArr.join(",")}}`

            clientSheetDataArr.push(tempObj)
            serverSheetDataArr.push(serverTempObj)
        }

        // 配置为json对象形式
        let clientTempTable = `"${sheetName}":[${clientSheetDataArr.join(",")}]`;
        let serverTempTable = `"${sheetName}":[${serverSheetDataArr.join(",")}]`;
        clientConfigList.push(clientTempTable);
        serverConfigList.push(serverTempTable);
    }

    // 表示有错误,终止剩下的操作
    if (!isAllSuccessful) {
        return false;
    }

    // 配置为json对象形式
    clientConfigTable += ("," + clientConfigList.join(","));
    serverConfigTable += ("," + serverConfigList.join(","));
    return true;
}

/** 
 * 获取C#对应的字段类型,类型可生成后酌情修改 - 只会拿前端的字段
 */
function getCSharpValueType(fieldTypeData, fieldName, sheetName, column) {
    let { field_type, output_type } = getOutAndFieldType(fieldTypeData, fieldName, sheetName, column);
    if (parseInt(output_type) == 2) {
        return { o: field_type, n: null };
    }
    switch (field_type) {
        case TypeEnum.string:
        case TypeEnum.bool:
        case TypeEnum.object:
            return { o: field_type, n: field_type };
        case TypeEnum.number:
            return { o: field_type, n: "float" };
        case TypeEnum.list:
            return { o: field_type, n: "List<object>" };
    }
}

/**
 * 获取输出字段类型和输出到前后端的类型
 * 
 * @param {*} fieldTypeData 固定取第3行的数据
 */
function getOutAndFieldType(fieldTypeData, fieldName, sheetName, column) {
    let hasError = false;
    // 以#号分割  第一个是字段类型 第二个是输出类型[默认为3]
    let column_types = fieldTypeData.split("#");
    let column_type_len = column_types.length;
    // client-1-输出到前端 server-2-输出到后端 all-3-全部（前端后端）
    let output_type;
    // 表示只有字段类型
    if (column_type_len == 1) {
        output_type = 3
        // 字段类型校验
        if (!checkIsLegalFieldType(column_types[0], sheetName, fieldName, column)) {
            hasError = true;
        }
    }
    // 第一个值为字段类型,第二个值为输出类型
    else if (column_type_len == 2) {
        // 字段类型校验
        if (!checkIsLegalFieldType(column_types[0], sheetName, fieldName, column)) {
            hasError = true;
        }
        // 输出类型
        output_type = column_types[1];
        if (output_type != "client" && output_type != "server" && output_type != "all") {
            errorCount++;
            console.error(`!!! [${errorCount}]错误:输出类型错误 => [${sheetName}] 第[3]行,[${numberMapCharTable.get(column)}]列的 [${fieldName}] 字段的输出类型错误[当前 = ${output_type}]!!! 只能为 client:仅前端 server:仅后端 all:前后端都要 或者省略不写!\n`);
            hasError = true;
        }
        switch (output_type) {
            case "client": output_type = 1; break;
            case "server": output_type = 2; break;
            case "all": output_type = 3; break;
        }
    }
    // 错误的赋值
    else {
        errorCount++;
        console.error(`!!! [${errorCount}]错误:字段和输出类型参数 => [${sheetName}] 第[3]行,[${numberMapCharTable.get(column)}]列的 [${fieldName}] 字段参数长度错误 [只能为1或2][当前 = ${column_type_len}]!!! 只能为[字段类型#输出类型],2个 或[字段类型],1个!\n`);
        hasError = true;
    }

    // 输出字段类型
    let field_type = column_types[0];
    // 返回类型对象
    return { output_type, field_type, hasError }
}

/**
 * 检查字段类型是否合法
 * @param {*} filed_type 
 * @param {*} sheetName 
 * @param {*} fieldName 
 * @returns 
 */
function checkIsLegalFieldType(filed_type, sheetName, fieldName, column) {
    if (TypeEnum.isIllegal(filed_type)) {
        errorCount++;
        console.error(`!!! [${errorCount}]错误:字段类型错误 => [${sheetName}] 第[${3}]行,[${numberMapCharTable.get(column)}]列的 [${fieldName}] 字段的类型错误[类型 = ${filed_type}]!!! 其类型只能是[${TypeEnum.getToString()}]其中之一!!! \n`);
        return false;
    }
    return true;
}

/**
 * 处理字段的值
 * 
 * @param {*} originVal 源数据
 * @param {*} fieldType 字段类型
 * @param {*} sheetName 表名字
 * @param {*} row 行
 * @param {*} column 列
 * @returns 
 */
function handleFieldVal(originVal, fieldType, fieldName, sheetPath, row, column) {
    let newData = null;
    let hasError = false;
    switch (fieldType) {
        case TypeEnum.string:
            newData = `"${fieldName}":"${originVal == undefined ? "" : originVal}"`
            break;
        case TypeEnum.number:
            let tempVall = `${(originVal == undefined || originVal == "") ? 0 : originVal}`;
            // console.log(tempVall);
            if (isNaN(Number(tempVall))) {
                hasError = true;
                logValueTypeError(sheetPath, row, column, originVal, TypeEnum.number, fieldName);
                break;
            }
            newData = `"${fieldName}":${tempVall}`;
            break;
        case TypeEnum.bool:
            if (!checkIsBool(originVal)) {
                hasError = true;
                logValueTypeError(sheetPath, row, column, originVal, TypeEnum.bool, fieldName);
                break;
            }
            newData = `"${fieldName}":${originVal.toString().toLowerCase()}`;
            break;
        case TypeEnum.list:
            let temp1 = (originVal == undefined || originVal == "") ? "null" : originVal;
            if (!checkIsListOrObjectType(temp1, TypeEnum.list)) {
                hasError = true;
                logValueTypeError(sheetPath, row, column, originVal, TypeEnum.list, fieldName);
                break;
            }
            newData = `"${fieldName}":${temp1}`;
            break;
        case TypeEnum.object:
            let temp2 = (originVal == undefined || originVal == "") ? "null" : originVal;

            if (!checkIsListOrObjectType(temp2, TypeEnum.object)) {
                hasError = true;
                logValueTypeError(sheetPath, row, column, originVal, TypeEnum.object, fieldName);
                break;
            }
            newData = `"${fieldName}":${temp2}`;
            break;
        default:
            hasError = true;
            console.error(`!!! 错误:字段类型错误 => [${sheetPath}]表 [${row + 1}]行 [${numberMapCharTable.get(column)}]列 ：字段值类型[${fieldName}:${fieldType}]错误,【${fieldType}】值只能是[${TypeEnum.getToString()}]这几种类型!!! \n源数据: ${originVal}`,);
            break;
    }

    return { hasError, newData }
}

/**
 * 数值和类型不匹配错误
 * @param {*} sheetPath 
 * @param {*} row 
 * @param {*} column 
 * @param {*} originVal 
 */
function logValueTypeError(sheetPath, row, column, originVal, typeDesc, fieldName) {
    console.error(`!!! [*]错误:值类型不匹配 => [${sheetPath}]表 第[${row + 1}]行,[${numberMapCharTable.get(column)}]列 [${fieldName}] 字段值类型被要求为【${typeDesc}】类型！但是值却不是${typeDesc}类型!!! \n\t源数据: ${originVal}\n`,);
}

/**
 * 检测当前字符串是否为数组和对象类型
 * @param {string} val 
 * @param {string} type 只能为："list" "Object"
 * @returns 
 */
function checkIsListOrObjectType(val, type = "list") {
    if (val == "null") {
        return true;
    }
    val = val.toString();
    if (type == "list") {
        if (!val.startsWith("[") || !val.endsWith("]")) {
            return false;
        }
    } else {
        if (!val.startsWith("{") || !val.endsWith("}")) {
            return false;
        }
    }

    try {
        JSON.parse(val);
        return true;
    } catch (error) {
        return false;
    }
}

/**
 * 检查是否为bool值
 * @param {*} val 
 * @returns 
 */
function checkIsBool(val) {
    if (val == null || val == undefined || (val == "" && val != false)) {
        return false;
    }
    val = val.toString().toUpperCase();
    if (val != "FALSE" && val != "TRUE") {
        return false;
    }
    return true;
}

/**
 * 客户端压缩并写出到次持久化路径
 * 
 * @param {*} dataStr 
 * @param {string} cfg 配置文件的string
 */
function clientOutput2Unity(dataStr, cfg) {
    // gzip方式压缩 - 
    let desc = cfg.split ? "【Split : " + cfg.name + "】" : "Client";
    let output_path, __checkDir;

    // 每个配置表单独输出
    if (cfg.split) {
        __checkDir = path.join(__dirname, "..", clientRootPath, "Split");
        output_path = path.join(__checkDir, cfg.name + ".split" + postfix);
    }
    // bcf 文件整体写出
    else {
        __checkDir = path.join(__dirname, "..", clientRootPath);
        output_path = path.join(__checkDir, clientConfigName + postfix);
    }

    // 处理未存在的文件夹
    Utils.checkAndMkDir(__checkDir);
    const input = stringToStream(Utils.XORDecrypt(dataStr, encryptCode));
    const output = fs.createWriteStream(output_path);
    const gzip = zlib.createGzip();

    input.pipe(gzip).pipe(output);

    if (!cfg.split) {
        console.log(`[${desc}] 配置文件输出完成！${output_path.toUpperCase()}`);
        createVersionVerify();
        createCSharpScript();
        copyHandler(output_path);
    } else {
        clientFileSplitCount++;
        if (clientFileSplitCount >= clientFileSplitTotal) {
            console.log("\n------- not important -------");
            console.log(`[Client] - Split 分表(${clientFileSplitCount}) - 输出完成！${__checkDir.toUpperCase()}`);
        }
    }
}

/**
 * 创建校验文件
 */
function createVersionVerify() {
    let enableEncrypt = (encryptCode != null && encryptCode != "");
    let verifyObj = {
        name: clientConfigName + postfix,
        size: (clientConfigTable.length / 1024 / 1024).toFixed(4) + "M",
        version: Date.now().toString(),
        EncryptCode: additionalEncryptCode ? encryptCode : "**********",
        EnableEncrypt: enableEncrypt,
        createTime: moment().format('YYYY年MM月DD日 HH:mm:ss'),
        tips: "该文件是.lcdat文件的校验文件,分离生成的.split文件是没有生成校验文件的!",
    };

    if (enableEncrypt) {
        console.log("\n[加密启用] 配置文件进行加密可能会略微增加大生成的文件的大小!!!");
        console.log(`[密钥] "${encryptCode}"`);
    }

    let verifyStr = JSON.stringify(verifyObj);
    fs.writeFileSync(path.join(__dirname, "..", clientRootPath, clientConfigName + postfix + "-verify.json"), verifyStr);
}

/**
 * 字符串转为Stream
 * @param {*} str 
 * @returns 
 */
function stringToStream(str) {
    const stream = new Readable();
    stream.push(str);
    stream.push(null);
    return stream;
}

/**
 * 复制生成的配置表到指定路径
 * 
 * @param {*} source_path 
 */
function copyHandler(source_path) {
    if (copyGeneratedConfigToDir == "" || copyGeneratedConfigToDir == null) {
        console.log("\n[Client] 跳过复制文件到指定目录:", copyGeneratedConfigToDir);
        return;
    }
    let distPath = copyGeneratedConfigToDir + "/" + clientConfigName + postfix;
    fs.copyFile(source_path, distPath, fs.constants.COPYFILE_REPLACE_EXISTING, (err) => {
        if (err) {
            console.log("\n------- copy file err-------\n");
            console.error("复制文件发生错误!", err);
            return;
        }
        console.log("\n------- copy the file -------");
        console.log("配置表文件成功复制到指定目录 =>" + distPath.replace("/", "\\").toUpperCase());
    })
}

/**
 * 自动生成C#脚本
 */
function createCSharpScript() {
    let csc = JSON.stringify(CSharpCodeContent);
    fs.writeFileSync(path.join(__dirname, "..", clientRootPath, "AutoGenerated.cs"), CSharpCodeContent);
}